Data
acc1_cancer_cells = readRDS("./Data/acc1_cancer_cells_15KnCount_V3.RDS")
all_acc_cancer_cells = readRDS("./Data/acc_cancer_cells_V3.RDS")
acc_all_cells = readRDS("./Data/acc_tpm_nCount_mito_no146_15k_with_ACC1_.RDS")
luminal_pathways = c("CHARAFE_BREAST_CANCER_LUMINAL_VS_BASAL_DN","CHARAFE_BREAST_CANCER_LUMINAL_VS_BASAL_UP","CHARAFE_BREAST_CANCER_LUMINAL_VS_MESENCHYMAL_DN","CHARAFE_BREAST_CANCER_LUMINAL_VS_MESENCHYMAL_UP","HUPER_BREAST_BASAL_VS_LUMINAL_DN","LIM_MAMMARY_LUMINAL_PROGENITOR_UP","SMID_BREAST_CANCER_LUMINAL_B_UP" )
# add luminal pathways
luminal_gs = msigdbr(species = "Homo sapiens") %>%as.data.frame() %>% dplyr::filter(gs_name %in% luminal_pathways)%>% dplyr::distinct(gs_name, gene_symbol) %>% as.data.frame()
Parameters
suffix = "01_25"
data_to_read = ""
functions
source_from_github(repositoy = "DEG_functions",version = "0.2.24")
ℹ SHA-1 hash of file is a183df1c565702ecd8ed338bb2abfb0e13415d8e
source_from_github(repositoy = "HMSC_functions",version = "0.1.12",script_name = "functions.R")
ℹ SHA-1 hash of file is 2934dee5f6b9fee69635192f19b1bcc205e05b62
source_from_github(repositoy = "cNMF_functions",version = "0.3.53",script_name = "cnmf_function_Harmony.R")
ℹ SHA-1 hash of file is a6f56985fe95e4026c7830d7100dd81dd950df3b
UMAP
DimPlot(object = acc1_cancer_cells,pt.size = 2)

features UMAP
FeaturePlot(object = acc1_cancer_cells,features = c("TP63","ACTA2","IL12B","CNN1"))
Warning in FeaturePlot(object = acc1_cancer_cells, features = c("TP63", :
All cells have the same value (0) of IL12B.

Enrichment analysis HMSC vs ACC
patient.ident = all_acc_cancer_cells$patient.ident %>% as.data.frame()
patient.ident[,1] = as.character(patient.ident[,1])
patient.ident[patient.ident[,1] == "ACC1",] = "HMSC"
patient.ident[,1] = as.factor(patient.ident[,1])
all_acc_cancer_cells = AddMetaData(object = all_acc_cancer_cells,metadata = patient.ident,col.name = "patient.ident")
all_acc_cancer_cells = SetIdent(all_acc_cancer_cells, value ="patient.ident")
acc_deg <- FindMarkers(all_acc_cancer_cells, ident.1 = "HMSC",logfc.threshold = 1.5,features = VariableFeatures(all_acc_cancer_cells))
enrichment_analysis(acc_deg,background = VariableFeatures(all_acc_cancer_cells),fdr_Cutoff = 0.01,ident.1 = "HMSC",ident.2 = "ACC",show_by = 1)

cell cycle filtering
hallmark_name = "GO_MITOTIC_CELL_CYCLE"
genesets =getGmt("./Data/h.all.v7.0.symbols.pluscc.gmt")
var_features=all_acc_cancer_cells@assays$RNA@var.features
geneIds= genesets[[hallmark_name]]@geneIds
score <- apply(all_acc_cancer_cells@assays$RNA@scale.data[intersect(geneIds,var_features),],2,mean)
all_acc_cancer_cells=AddMetaData(all_acc_cancer_cells,score,hallmark_name)
#filter:
all_acc_cancer_cells_ccFiltered=all_acc_cancer_cells[,all_acc_cancer_cells@meta.data[[hallmark_name]]< 0.3]
min_threshold = min(all_acc_cancer_cells$GO_MITOTIC_CELL_CYCLE)
max_threshold = max(all_acc_cancer_cells$GO_MITOTIC_CELL_CYCLE)
Before cc filtering
FeaturePlot(object = all_acc_cancer_cells,features = hallmark_name) + ggtitle("Before cc filtering") & scale_color_gradientn(colours = plasma(n = 10, direction = -1), limits = c(min_threshold, max_threshold))
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

After cc filtering
FeaturePlot(object = all_acc_cancer_cells_ccFiltered,features = hallmark_name) + ggtitle("After cc filtering") & scale_color_gradientn(colours = plasma(n = 10, direction = -1), limits = c(min_threshold, max_threshold))
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

Enrichment analysis filtered HMSC vs ACC
patient.ident = all_acc_cancer_cells_ccFiltered$patient.ident %>% as.data.frame()
patient.ident[,1] = as.character(patient.ident[,1])
patient.ident[patient.ident[,1] == "ACC1",] = "HMSC"
patient.ident[,1] = as.factor(patient.ident[,1])
all_acc_cancer_cells_ccFiltered = AddMetaData(object = all_acc_cancer_cells_ccFiltered,metadata = patient.ident,col.name = "patient.ident")
all_acc_cancer_cells_ccFiltered = SetIdent(all_acc_cancer_cells_ccFiltered, value ="patient.ident")
acc_deg <- FindMarkers(all_acc_cancer_cells_ccFiltered, ident.1 = "HMSC",logfc.threshold = 1.5,features = VariableFeatures(all_acc_cancer_cells_ccFiltered))
enrichment_analysis(acc_deg,background = VariableFeatures(all_acc_cancer_cells_ccFiltered),fdr_Cutoff = 0.01,ident.1 = "HMSC",ident.2 = "ACC",show_by = 1)

MYB expression
all_acc_cancer_cells = SetIdent(object = all_acc_cancer_cells,value = "patient.ident") #active snn graph
FeaturePlot(object = all_acc_cancer_cells,features = "MYB",label = T)

CNV
new.cluster.ids <- c("cancer", #0
"cancer", #1
"CAF", #2
"cancer", #3
"Endothelial", #4
"cancer", #5
"cancer", #6
"CAF", #7
"CAF", #8
"CAF", #9
"cancer", #10
"CAF", #11
"cancer", #12
"cancer", #13
"cancer", #14
"cancer", #15
"cancer", #16
"WBC", #17
"CAF" #18
)
#rename idents:
acc_all_cells = SetIdent(object = acc_all_cells,value = "RNA_snn_res.1") #active snn graph
names(new.cluster.ids) <- levels(acc_all_cells) #add snn graph levels to new.cluster.ids
acc_all_cells@meta.data[["seurat_clusters"]] = acc_all_cells@meta.data[["RNA_snn_res.1"]]
acc_all_cells = SetIdent(object = acc_all_cells,value = "seurat_clusters")
acc_all_cells <- RenameIdents(acc_all_cells, new.cluster.ids)
# divide "cancer" into patients:
cell_types = acc_all_cells@active.ident %>% as.data.frame()
cell_types[,1]<- as.character(cell_types[,1])
cell_types = cbind(cell_types,acc_all_cells$patient.ident) %>% setnames(old = names(.),
new = c('cell_type','patient'))
cell_types[cell_types$cell_type == "cancer",] = cell_types[cell_types$cell_type == "cancer",2]
# hmsc_rows = (startsWith(x = rownames(cell_types),prefix = "ACC.plate2") | startsWith(x = rownames(cell_types),prefix = "ACC1.")) & cell_types[,1] == "cancer"
# acc_rows = !(startsWith(x = rownames(cell_types),prefix = "ACC.plate2") | startsWith(x = rownames(cell_types),prefix = "ACC1.")) & cell_types[,1] == "cancer"
# cell_types[,1][hmsc_rows] = "HMSC"
# cell_types[,1][acc_rows] = "ACC"
#add to metadata:
cell_types[,2] = NULL
cell_types[cell_types$cell_type == "ACC1",] = "HMSC"
acc_all_cells = AddMetaData(object =acc_all_cells ,metadata = cell_types,col.name = "cell.type")
CNV UMAP

CNV plot
## {-}
CNV subtypes
cnv_subtypes = as.data.frame(cutree(infercnv_obj_default@tumor_subclusters[["hc"]][["HMSC"]], k = 2))
names(cnv_subtypes)[1] = "cnv.cluster"
rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "-",replacement = "\\.")
infercnv.observations = data.frame(fread(file = "./Data/inferCNV/infercnv.observations.txt"), row.names=1)
Warning in fread(file = "./Data/inferCNV/infercnv.observations.txt") :
Detected 1332 column names but the data has 1333 columns (i.e. invalid file). Added 1 extra default column name for the first column which is guessed to be row names or an index. Use setnames() afterwards if this guess is not correct, or fix the file write command that created the file to create a valid file.
names_to_keep = colnames(infercnv.observations) %in% (colnames(acc1_cancer_cells) %>% gsub(pattern = "_",replacement = "\\."))
infercnv.observations = infercnv.observations[,names_to_keep]
rotate <- function(x) t(apply(x, 2, rev))
infercnv.observations2 = infercnv.observations %>% rotate() %>% rotate() %>% rotate()%>% as.data.frame()
breaks = c(0.700891861704857,
0.742366945528369,
0.783842029351881,
0.825317113175393,
0.866792196998905,
0.908267280822417,
0.949742364645928,
0.99121744846944,
1.03269253229295,
1.07416761611646,
1.11564269993998,
1.15711778376349,
1.198592867587,
1.24006795141051,
1.28154303523402,
1.32301811905753)
pheatmap(infercnv.observations2,cluster_cols = F,cluster_rows = F, show_rownames = F,show_colnames = F, breaks = breaks,color = colorRampPalette(rev(c("darkred", "white", "darkblue")))(15),annotation_row = cnv_subtypes)

rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "2\\.",replacement = "2_")
rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "3\\.",replacement = "3_")
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = cnv_subtypes)
DimPlot(acc1_cancer_cells,group.by = "cnv.cluster",pt.size = 2,cols =colors)

Original score
original_myo_genes = c( "TP63", "TP73", "CAV1", "CDH3", "KRT5", "KRT14", "ACTA2", "TAGLN", "MYLK", "DKK3")
original_lum_genes = c("KIT", "EHF", "ELF5", "KRT7", "CLDN3", "CLDN4", "CD24", "LGALS3", "LCN2", "SLPI" )
calculate_score(dataset = all_acc_cancer_cells,myo_genes = original_myo_genes,lum_genes = original_lum_genes)
correlation of lum score and myo score: -0.45
correlation of lum score and original lum score: 1
correlation of myo score and original myo score: 1



Original score of ACC1
calculate_score(dataset = acc1_cancer_cells,myo_genes = original_myo_genes,lum_genes = original_lum_genes,lum_threshold = 0,myo_threshold = 0)
correlation of lum score and myo score: 0.06
correlation of lum score and original lum score: 1
correlation of myo score and original myo score: 1



0.35 Most correlated score
Myo genes
myo_protein_markers = c("CNN1", "TP63","ACTA2")
top_myo = top_correlated(dataset = acc1_cancer_cells, genes = myo_protein_markers,threshold = 0.35)
print("Number of genes = " %>% paste(length(top_myo)))
[1] "Number of genes = 20"
message("Names of genes:")
Names of genes:
top_myo %>% head(30)
[1] "COL16A1" "RP1-39G22.4" "ACTG2" "CD200" "MYLK" "TP63" "KCNMB1"
[8] "ADAMTS2" "LOXL2" "TPM2" "CLIC3" "SNCG" "ACTA2" "TAGLN"
[15] "A2M" "NGFR" "CNN1" "PPP1R14A" "MYL9" "POM121L9P"
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_myo,original_myo_genes)
[1] "MYLK" "TP63" "ACTA2" "TAGLN"
myo_enrich_res = genes_vec_enrichment(genes = top_myo,background = rownames(acc1_cancer_cells),homer = T,title = "myo top enrichment",custom_pathways = luminal_gs)

myo_enrich_res
Lum genes
lum_protein_markers = c("KIT")
top_lum = top_correlated(dataset = acc1_cancer_cells, genes = lum_protein_markers,threshold = 0.35,n_vargenes = 5000)
Calculating gene variances
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
print("Number of genes = " %>% paste(length(top_lum)))
[1] "Number of genes = 5"
message("Names of genes:")
Names of genes:
top_lum %>% head(30)
[1] "B3GNT2" "GLRB" "EFNA5" "ALDH3B2" "CCND1"
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_lum,original_lum_genes)
character(0)
lum_enrich_res = genes_vec_enrichment(genes = top_lum,background = rownames(acc1_cancer_cells),homer = T,title = "lum top enrichment",custom_pathways = luminal_gs)

lum_enrich_res
enriched genes score
rownames(lum_enrich_res) = lum_enrich_res$pathway_name
lum_enriched_genes = lum_enrich_res[1,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,lum_protein_markers) #add original markers
rownames(myo_enrich_res) = myo_enrich_res$pathway_name
myo_enriched_genes = myo_enrich_res[1,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,myo_protein_markers) #add original markers
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = -2.5,myo_threshold = -2.5)
correlation of lum score and myo score: -0.16
correlation of lum score and original lum score: 0.43
correlation of myo score and original myo score: 0.79



0.2 Most correlated score
myo Genes
n_vargenes = 2000
myo_protein_markers = c("CNN1", "TP63","ACTA2")
top_myo = top_correlated(dataset = acc1_cancer_cells, genes = myo_protein_markers,threshold = 0.2,n_vargenes = n_vargenes)
print("Number of genes = " %>% paste(length(top_myo)))
[1] "Number of genes = 48"
message("Names of genes:")
Names of genes:
top_myo %>% head(30)
[1] "PLOD1" "RP1-39G22.4" "PDZK1" "CSRP1" "CHI3L1" "HIST3H3"
[7] "AC104699.1" "ACTG2" "LYG1" "DALRD3" "CD200" "RP11-627C21.1"
[13] "FGFBP2" "IGFBP7" "HSPB3" "RP11-168A11.4" "SPARC" "CD83"
[19] "HLA-DOB" "TBCC" "NFKBIE" "MIR3662" "SMOC2" "FBXL6"
[25] "TPM2" "SNCG" "ACTA2" "DKK3" "MIR7113" "CTA-797E19.3"
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_myo,original_myo_genes)
[1] "ACTA2" "DKK3" "TAGLN"
myo_enrich_res = genes_vec_enrichment(genes = top_myo,background = VariableFeatures(acc1_cancer_cells) %>% head(n_vargenes),homer = T,title = "myo top enrichment",custom_pathways = luminal_gs)

myo_enrich_res
Lum Genes
acc1_cancer_cells = FindVariableFeatures(object = acc1_cancer_cells,nfeatures = 15000)
Calculating gene variances
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
lum_protein_markers = c("KIT")
n_vargenes = 2000
top_lum = top_correlated(dataset = acc1_cancer_cells, genes = lum_protein_markers,threshold = 0.3,n_vargenes = n_vargenes)
print("Number of genes = " %>% paste(length(top_lum)))
[1] "Number of genes = 13"
message("Names of genes:")
Names of genes:
top_lum %>% head(30)
[1] "CSDE1" "RRP9" "TKT" "TFG" "ATP1B3" "EFNA5" "GABRP" "CALML5" "MMP7" "SNORD68"
[11] "APP" "SDF2L1" "SAT1"
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_lum,original_lum_genes)
character(0)
lum_enrich_res = genes_vec_enrichment(genes = top_lum,background = VariableFeatures(acc1_cancer_cells) %>% head(n_vargenes),homer = T,title = "lum top enrichment",custom_pathways = luminal_gs)

lum_enrich_res
calculate_score(dataset = acc1_cancer_cells,myo_genes = top_myo,lum_genes = top_lum,lum_threshold = 2,myo_threshold = 1)
correlation of lum score and myo score: -0.02
correlation of lum score and original lum score: 0.65
correlation of myo score and original myo score: 0.77



myo_intersected = intersect(top_myo,original_myo_genes)
lum_intersected = intersect(top_lum,original_lum_genes)
message("genes in myo score:")
genes in myo score:
myo_intersected
[1] "ACTA2" "DKK3" "TAGLN"
message("genes in lum score:")
genes in lum score:
lum_intersected
[1] "LGALS3"
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_intersected,lum_genes = lum_intersected,lum_threshold = 2,myo_threshold = 1)
correlation of lum score and myo score: 0.03
correlation of lum score and original lum score: 0.69
correlation of myo score and original myo score: 0.81



enriched genes
rownames(lum_enrich_res) = lum_enrich_res$pathway_name
lum_enriched_genes = lum_enrich_res[3,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,lum_protein_markers) #add original markers
rownames(myo_enrich_res) = myo_enrich_res$pathway_name
myo_enriched_genes = myo_enrich_res[3,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,myo_protein_markers) #add original markers
message("genes in myo score:")
genes in myo score:
myo_enriched_genes
[1] "FBLIM1" "NEXN" "NMNAT2" "MYLK" "CCDC50" "IGFBP7" "RAI14" "ARAP3" "SPARC" "CALD1" "LOXL2"
[12] "COL5A1" "ACTA2" "DKK3" "MSRB3" "COL4A1" "ACTN1" "TPM1" "TGFB1I1" "ADORA2B" "MXRA7" "CNN1"
[23] "TP63" "ACTA2"
message("genes in lum score:")
genes in lum score:
lum_enriched_genes
[1] "PADI2" "PATJ" "PEX11B" "APH1A" "C1orf43" "EFNA4" "NECTIN4" "SCYL3" "ELF3"
[10] "SOX13" "IRF6" "MED28" "EPB41L4A" "RGL2" "C6orf132" "TPD52L1" "ICA1" "MACC1"
[19] "TRPS1" "FAM83H" "RASEF" "ARRDC1" "COMMD3" "ANK3" "GSTO2" "PDCD4" "EHF"
[28] "ALDH3B2" "SHANK2" "SORL1" "FKBP4" "PTPN6" "DUSP16" "RERG" "ADCY6" "ERBB3"
[37] "ERP29" "SUSD6" "RPS6KA5" "SPINT1" "FEM1B" "TLE3" "SCAMP2" "CLN3" "ADGRG1"
[46] "ATP2C2" "GGT6" "MYO1D" "ST6GALNAC2" "CYB5A" "BLVRB" "VRK3" "SYCP2" "TMPRSS2"
[55] "LIMK2" "KIT"
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = 0,myo_threshold = -1)
correlation of lum score and myo score: 0.1
correlation of lum score and original lum score: 0.71
correlation of myo score and original myo score: 0.76



enriched genes and in original score
myo_enriched_genes = myo_enriched_genes[myo_enriched_genes %in% original_myo_genes]
lum_enriched_genes = lum_enriched_genes[lum_enriched_genes %in% original_lum_genes]
message("genes in myo score:")
genes in myo score:
myo_enriched_genes
[1] "MYLK" "ACTA2" "DKK3" "TP63" "ACTA2"
message("genes in lum score:")
genes in lum score:
lum_enriched_genes
[1] "EHF" "KIT"
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = 2,myo_threshold = 2)
correlation of lum score and myo score: -0.07
correlation of lum score and original lum score: 0.62
correlation of myo score and original myo score: 0.77



HPV
Only HMSC cancer cells:
HPV33_P3 = fread("./Data/HPV33_P3.txt",col.names = c("plate","reads")) %>% as.data.frame()
HPV33_P3.df = HPV33_P3 %>% mutate(
plate = gsub(x =HPV33_P3$plate, replacement = "",pattern = "_.*$")
%>% gsub(pattern = "-P",replacement = ".P")
%>% gsub(pattern = "-",replacement = "_",)
)
HPV33_P3.df = HPV33_P3.df %>% dplyr::filter(HPV33_P3.df$plate %in% colnames(acc1_cancer_cells))
rownames(HPV33_P3.df) <- HPV33_P3.df$plate
HPV33_P3.df$plate = NULL
HPV33_P2 = fread("./Data/HPV33_P2.txt",col.names = c("plate","reads")) %>% as.data.frame()
HPV33_P2.df = HPV33_P2 %>% mutate(
plate = gsub(x =HPV33_P2$plate, replacement = "",pattern = "_.*$")
%>% gsub(pattern = "plate2-",replacement = "plate2_",)
%>% gsub(pattern = "-",replacement = "\\.",)
)
HPV33_P2.df = HPV33_P2.df %>% dplyr::filter(HPV33_P2.df$plate %in% colnames(acc1_cancer_cells))
rownames(HPV33_P2.df) <- HPV33_P2.df$plate
HPV33_P2.df$plate = NULL
HPV33 = rbind(HPV33_P3.df,HPV33_P2.df)
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = HPV33,col.name = "HPV33.reads")
FeaturePlot(acc1_cancer_cells,features = "HPV33.reads",max.cutoff = 40)

data = FetchData(object = acc1_cancer_cells,vars = "HPV33.reads")
print(
data %>%
ggplot(aes( x=HPV33.reads)) +
geom_density()
)

hpv33_positive = HPV33 %>% dplyr::mutate(hpv33_positive = case_when(reads >= 750 ~"strong positive",
reads < 750 & reads > 10 ~ "positive",
reads < 10 ~ "negative")
)
hpv33_positive$reads = NULL
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = hpv33_positive)
DimPlot(object = acc1_cancer_cells,group.by = c("hpv33_positive"),pt.size = 2)

cNMF
library(reticulate)
#write expression
acc1_cancer_cells = FindVariableFeatures(object = acc1_cancer_cells,nfeatures = 2000)
vargenes = VariableFeatures(object = acc1_cancer_cells)
hmsc_expression = t(as.matrix(GetAssayData(acc1_cancer_cells,slot='data')))
hmsc_expression = 2**hmsc_expression #convert from log2(tpm+1) to tpm
hmsc_expression = hmsc_expression-1
# hmsc_expression = hmsc_expression[,!colSums(hmsc_expression==0, na.rm=TRUE)==nrow(hmsc_expression)] #delete rows that have all 0
hmsc_expression = hmsc_expression[,vargenes]
write.table(x = hmsc_expression ,file = './Data/cNMF/hmsc_expressionData_2Kvargenes.txt',sep = "\t")
from cnmf import cNMF
name = 'HMSC_cNMF_2Kvargenes'
outdir = './Data/cNMF'
K_range = np.arange(3,10)
cnmf_obj = cNMF(output_dir=outdir, name=name)
counts_fn='./Data/cNMF/hmsc_expressionData_2Kvargenes.txt'
tpm_fn = counts_fn ## This is a weird case where because this dataset is not 3' end umi sequencing, we opted to use the TPM matrix as the input matrix rather than the count matrix
cnmf_obj.prepare(counts_fn=counts_fn, components=K_range, seed=14,tpm_fn=tpm_fn)
cnmf_obj.factorize(worker_i=0, total_workers=1)
cnmf_obj.combine()
cnmf_obj.k_selection_plot()
Save object
import pickle
f = open('./Data/cNMF/HMSC_cNMF_2Kvargenes/cnmf_obj.pckl', 'wb')
pickle.dump(cnmf_obj, f)
f.close()
Load object
from cnmf import cNMF
import pickle
f = open('./Data/cNMF/HMSC_cNMF_2Kvargenes/cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()
selected_k = 4
density_threshold = 0.1
cnmf_obj.consensus(k=selected_k, density_threshold=density_threshold,show_clustering=True)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/lib/python3.7/site-packages/scanpy/preprocessing/_simple.py:843: UserWarning: Received a view of an AnnData. Making a copy.
view_to_actual(adata)
usage_norm, gep_scores, gep_tpm, topgenes = cnmf_obj.load_results(K=selected_k, density_threshold=density_threshold)
# calculate usage by expression*genes coefs:
gep_scores = py$gep_scores
all_metagenes= py$usage_norm
Enrichment analysis by top 200 genes of each program
plt_list = list()
for (i in 1:ncol(gep_scores)) {
top_genes = gep_scores %>% arrange(desc(gep_scores[i])) #sort by score a
top = head(rownames(top_genes),200) #take top top_genes_num
res = genes_vec_enrichment(genes = top,background = rownames(gep_scores),homer = T,title =
i,silent = T,return_all = T)
plt_list[[i]] = res$plt
}
gridExtra::grid.arrange(grobs = plt_list)

plt_list = list()
for (i in 1:ncol(gep_scores)) {
top_genes = gep_scores %>% arrange(desc(gep_scores[i])) #sort by score a
top = head(rownames(top_genes),10) #take top top_genes_num
message(paste("program ",i,"top genes:"))
print(top)
}
program 1 top genes:
[1] "IGKV5-2" "NDUFB7" "LGALS1" "UBL5" "S100A6" "S100A11" "CUTA" "IFITM3" "JTB" "IL17RB"
program 2 top genes:
[1] "EGR1" "C6orf62" "JUNB" "DNAJA1" "ATF3" "IER2" "ERRFI1" "KLF6" "CDKN1A" "MTHFD2"
program 3 top genes:
[1] "RP1-128M12.3" "RP11-374F3.3" "RP11-403A21.3" "RP11-454L9.2" "AC097500.1" "RP11-463H24.4"
[7] "RP11-515O17.2" "RP11-536L3.4" "PALD1" "AL049758.2"
program 4 top genes:
[1] "LTF" "PIGR" "FMO2" "HSD17B2" "MLPH" "PRR15L"
[7] "RF00019.219" "RP11-817J15.2" "CD14" "CLU"
# Make metagene names
for (i in 1:ncol(all_metagenes)) {
colnames(all_metagenes)[i] = "metagene." %>% paste0(i)
}
#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
metage_metadata = all_metagenes %>% select(i)
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = metage_metadata)
}
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(i)` instead of `i` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
FeaturePlot(object = acc1_cancer_cells,features = colnames(all_metagenes))

assignment UMAP
larger_by = 2
message(paste("larger_by = ", larger_by))
larger_by = 2
acc1_cancer_cells = program_assignment(dataset = acc1_cancer_cells,larger_by = larger_by,program_names = colnames(all_metagenes))
selected_k =4
colors = rainbow(selected_k)
colors = c(colors,"grey")
DimPlot(acc1_cancer_cells,group.by = "program.assignment",pt.size = 2,cols =colors)

show cell cycle program:
hallmark_name = "GO_MITOTIC_CELL_CYCLE"
genesets =GSEABase::getGmt("./Data/h.all.v7.0.symbols.pluscc.gmt")
var_features=acc1_cancer_cells@assays$RNA@var.features
geneIds= genesets[[hallmark_name]]@geneIds
score <- apply(acc1_cancer_cells@assays$RNA@data[intersect(geneIds,var_features),],2,mean)
acc1_cancer_cells=AddMetaData(acc1_cancer_cells,score,hallmark_name)
FeaturePlot(object = acc1_cancer_cells,features = hallmark_name)

cc_vs_program2 = FetchData(object = acc1_cancer_cells,vars = c("metagene.2",hallmark_name))
cor(cc_vs_program2[1],cc_vs_program2[2])
GO_MITOTIC_CELL_CYCLE
metagene.2 0.611673
Comparisions
cnv_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","hpv33_positive"))
test <- fisher.test(table(cnv_vs_hpv))
ggbarstats(
cnv_vs_hpv, cnv.cluster, hpv33_positive,
results.subtitle = FALSE,
subtitle = paste0(
"Fisher's exact test", ", p-value = ",
ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
)
)

cnv_vs_cnmf = FetchData(object = acc1_cancer_cells,vars = c("program.assignment","cnv.cluster"))
cnv_vs_cnmf = cnv_vs_cnmf %>% dplyr::filter(program.assignment == "1" |program.assignment == "2" )
test <- fisher.test(table(cnv_vs_cnmf))
ggbarstats(
cnv_vs_cnmf, program.assignment, cnv.cluster,
results.subtitle = FALSE,
subtitle = paste0(
"Fisher's exact test", ", p-value = ",
ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
)
)

cnmf_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("program.assignment","hpv33_positive"))
cnmf_vs_hpv = cnmf_vs_hpv %>% dplyr::filter(program.assignment == "1" |program.assignment == "2" )
test <- fisher.test(table(cnmf_vs_hpv))
ggbarstats(
cnmf_vs_hpv, program.assignment, hpv33_positive,
results.subtitle = FALSE,
subtitle = paste0(
"Fisher's exact test", ", p-value = ",
ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
)
)

myb_vs_cnv = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","MYB"))
myb_vs_cnv $cnv.cluster = as.character(myb_vs_cnv $cnv.cluster )
ggboxplot(myb_vs_cnv, x = "cnv.cluster", y = "MYB",
palette = "jco",
add = "jitter")+ stat_compare_means(method = "wilcox.test",comparisons = list(c("1","2")))

myb_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("hpv33_positive","MYB"))
myb_vs_hpv $hpv33_positive = as.character(myb_vs_hpv $hpv33_positive )
ggboxplot(myb_vs_hpv, x = "hpv33_positive", y = "MYB",
palette = "jco",
add = "jitter")+ stat_compare_means(method = "wilcox.test",comparisons = list(c("positive","negative"),c("strong positive", "positive"),c("strong positive", "negative")))+ stat_summary(fun.data = function(x) data.frame(y=15, label = paste("Mean=",round(mean(x),digits = 2))), geom="text") +ylab("log2(MYB)")
Warning in wilcox.test.default(c(7.30445531374182, 0, 7.65159269158999, :
cannot compute exact p-value with ties

hpvReads_vs_myb = FetchData(object = acc1_cancer_cells,vars = c("HPV33.reads","MYB"))
corr = cor(hpvReads_vs_myb$HPV33.reads,hpvReads_vs_myb$MYB)
print("correlation of MYB abd hpv33_reads:" %>% paste(corr %>% round(digits = 2)) )
[1] "correlation of MYB abd hpv33_reads: 0.21"
LS0tCnRpdGxlOiAiVGl0bGUiCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KYGBge3IsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBpbmNsdWRlID0gVAopCmBgYAoKIyMgRGF0YQoKYGBge3J9CmFjYzFfY2FuY2VyX2NlbGxzID0gcmVhZFJEUygiLi9EYXRhL2FjYzFfY2FuY2VyX2NlbGxzXzE1S25Db3VudF9WMy5SRFMiKQphbGxfYWNjX2NhbmNlcl9jZWxscyA9IHJlYWRSRFMoIi4vRGF0YS9hY2NfY2FuY2VyX2NlbGxzX1YzLlJEUyIpCmFjY19hbGxfY2VsbHMgPSByZWFkUkRTKCIuL0RhdGEvYWNjX3RwbV9uQ291bnRfbWl0b19ubzE0Nl8xNWtfd2l0aF9BQ0MxXy5SRFMiKQpsdW1pbmFsX3BhdGh3YXlzID0gYygiQ0hBUkFGRV9CUkVBU1RfQ0FOQ0VSX0xVTUlOQUxfVlNfQkFTQUxfRE4iLCJDSEFSQUZFX0JSRUFTVF9DQU5DRVJfTFVNSU5BTF9WU19CQVNBTF9VUCIsIkNIQVJBRkVfQlJFQVNUX0NBTkNFUl9MVU1JTkFMX1ZTX01FU0VOQ0hZTUFMX0ROIiwiQ0hBUkFGRV9CUkVBU1RfQ0FOQ0VSX0xVTUlOQUxfVlNfTUVTRU5DSFlNQUxfVVAiLCJIVVBFUl9CUkVBU1RfQkFTQUxfVlNfTFVNSU5BTF9ETiIsIkxJTV9NQU1NQVJZX0xVTUlOQUxfUFJPR0VOSVRPUl9VUCIsIlNNSURfQlJFQVNUX0NBTkNFUl9MVU1JTkFMX0JfVVAiICkKCiMgYWRkIGx1bWluYWwgcGF0aHdheXMKbHVtaW5hbF9ncyA9IG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiKSAlPiVhcy5kYXRhLmZyYW1lKCkgJT4lIGRwbHlyOjpmaWx0ZXIoZ3NfbmFtZSAlaW4lIGx1bWluYWxfcGF0aHdheXMpJT4lIGRwbHlyOjpkaXN0aW5jdChnc19uYW1lLCBnZW5lX3N5bWJvbCkgJT4lIGFzLmRhdGEuZnJhbWUoKQoKYGBgCgojIyBQYXJhbWV0ZXJzCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpzdWZmaXggPSAiMDFfMjUiCmRhdGFfdG9fcmVhZCA9ICIiCmBgYAoKCiMjIGZ1bmN0aW9ucwoKYGBge3Igd2FybmluZz1GQUxTRX0Kc291cmNlX2Zyb21fZ2l0aHViKHJlcG9zaXRveSA9ICJERUdfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMi4yNCIpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiSE1TQ19mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4xLjEyIixzY3JpcHRfbmFtZSA9ICJmdW5jdGlvbnMuUiIpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiY05NRl9mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4zLjUzIixzY3JpcHRfbmFtZSA9ICJjbm1mX2Z1bmN0aW9uX0hhcm1vbnkuUiIpCmBgYAojIyBVTUFQCmBgYHtyIH0KRGltUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxwdC5zaXplID0gMikKYGBgCiMjIGZlYXR1cmVzIFVNQVAKYGBge3IgZmlnLndpZHRoPTEwfQpGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGMoIlRQNjMiLCJBQ1RBMiIsIklMMTJCIiwiQ05OMSIpKQpgYGAKCgojIyBFbnJpY2htZW50IGFuYWx5c2lzIEhNU0MgdnMgQUNDCmBgYHtyIGZpZy53aWR0aD04LCBlY2hvPVRSVUUscmVzdWx0cz0naGlkZScsZmlnLmtlZXA9J2FsbCd9CnBhdGllbnQuaWRlbnQgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyRwYXRpZW50LmlkZW50ICU+JSBhcy5kYXRhLmZyYW1lKCkKcGF0aWVudC5pZGVudFssMV0gPSBhcy5jaGFyYWN0ZXIocGF0aWVudC5pZGVudFssMV0pCnBhdGllbnQuaWRlbnRbcGF0aWVudC5pZGVudFssMV0gPT0gIkFDQzEiLF0gPSAiSE1TQyIKcGF0aWVudC5pZGVudFssMV0gPSBhcy5mYWN0b3IocGF0aWVudC5pZGVudFssMV0pCmFsbF9hY2NfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWxsX2FjY19jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBwYXRpZW50LmlkZW50LGNvbC5uYW1lID0gInBhdGllbnQuaWRlbnQiKQphbGxfYWNjX2NhbmNlcl9jZWxscyA9IFNldElkZW50KGFsbF9hY2NfY2FuY2VyX2NlbGxzLCB2YWx1ZSA9InBhdGllbnQuaWRlbnQiKQphY2NfZGVnIDwtIEZpbmRNYXJrZXJzKGFsbF9hY2NfY2FuY2VyX2NlbGxzLCBpZGVudC4xID0gIkhNU0MiLGxvZ2ZjLnRocmVzaG9sZCA9IDEuNSxmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMoYWxsX2FjY19jYW5jZXJfY2VsbHMpKQplbnJpY2htZW50X2FuYWx5c2lzKGFjY19kZWcsYmFja2dyb3VuZCA9IFZhcmlhYmxlRmVhdHVyZXMoYWxsX2FjY19jYW5jZXJfY2VsbHMpLGZkcl9DdXRvZmYgPSAwLjAxLGlkZW50LjEgPSAiSE1TQyIsaWRlbnQuMiA9ICJBQ0MiLHNob3dfYnkgPSAxKQpgYGAKCiMjIGNlbGwgY3ljbGUgZmlsdGVyaW5nIHsudGFic2V0fQoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmhhbGxtYXJrX25hbWUgPSAiR09fTUlUT1RJQ19DRUxMX0NZQ0xFIgpnZW5lc2V0cyAgPWdldEdtdCgiLi9EYXRhL2guYWxsLnY3LjAuc3ltYm9scy5wbHVzY2MuZ210IikKdmFyX2ZlYXR1cmVzPWFsbF9hY2NfY2FuY2VyX2NlbGxzQGFzc2F5cyRSTkFAdmFyLmZlYXR1cmVzCmdlbmVJZHM9IGdlbmVzZXRzW1toYWxsbWFya19uYW1lXV1AZ2VuZUlkcwpzY29yZSA8LSBhcHBseShhbGxfYWNjX2NhbmNlcl9jZWxsc0Bhc3NheXMkUk5BQHNjYWxlLmRhdGFbaW50ZXJzZWN0KGdlbmVJZHMsdmFyX2ZlYXR1cmVzKSxdLDIsbWVhbikKYWxsX2FjY19jYW5jZXJfY2VsbHM9QWRkTWV0YURhdGEoYWxsX2FjY19jYW5jZXJfY2VsbHMsc2NvcmUsaGFsbG1hcmtfbmFtZSkKCiNmaWx0ZXI6CmFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQ9YWxsX2FjY19jYW5jZXJfY2VsbHNbLGFsbF9hY2NfY2FuY2VyX2NlbGxzQG1ldGEuZGF0YVtbaGFsbG1hcmtfbmFtZV1dPCAwLjNdCgoKbWluX3RocmVzaG9sZCA9IG1pbihhbGxfYWNjX2NhbmNlcl9jZWxscyRHT19NSVRPVElDX0NFTExfQ1lDTEUpCm1heF90aHJlc2hvbGQgPSBtYXgoYWxsX2FjY19jYW5jZXJfY2VsbHMkR09fTUlUT1RJQ19DRUxMX0NZQ0xFKQpgYGAKCgojIyMgQmVmb3JlIGNjIGZpbHRlcmluZwoKCmBgYHtyfQpGZWF0dXJlUGxvdChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGhhbGxtYXJrX25hbWUpICsgZ2d0aXRsZSgiQmVmb3JlIGNjIGZpbHRlcmluZyIpICAmIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzID0gcGxhc21hKG4gPSAxMCwgZGlyZWN0aW9uID0gLTEpLCBsaW1pdHMgPSBjKG1pbl90aHJlc2hvbGQsIG1heF90aHJlc2hvbGQpKQpgYGAKCiMjIyBBZnRlciBjYyBmaWx0ZXJpbmcKCmBgYHtyfQpGZWF0dXJlUGxvdChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkLGZlYXR1cmVzID0gaGFsbG1hcmtfbmFtZSkgKyBnZ3RpdGxlKCJBZnRlciBjYyBmaWx0ZXJpbmciKSAmIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzID0gcGxhc21hKG4gPSAxMCwgZGlyZWN0aW9uID0gLTEpLCBsaW1pdHMgPSBjKG1pbl90aHJlc2hvbGQsIG1heF90aHJlc2hvbGQpKQpgYGAKIyMgey19CgojIyBFbnJpY2htZW50IGFuYWx5c2lzIGZpbHRlcmVkIEhNU0MgdnMgQUNDCmBgYHtyIGZpZy53aWR0aD04LCBlY2hvPVRSVUUscmVzdWx0cz0naGlkZScsZmlnLmtlZXA9J2FsbCd9CnBhdGllbnQuaWRlbnQgPSBhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkJHBhdGllbnQuaWRlbnQgJT4lIGFzLmRhdGEuZnJhbWUoKQpwYXRpZW50LmlkZW50WywxXSA9IGFzLmNoYXJhY3RlcihwYXRpZW50LmlkZW50WywxXSkKcGF0aWVudC5pZGVudFtwYXRpZW50LmlkZW50WywxXSA9PSAiQUNDMSIsXSA9ICJITVNDIgpwYXRpZW50LmlkZW50WywxXSA9IGFzLmZhY3RvcihwYXRpZW50LmlkZW50WywxXSkKYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQsbWV0YWRhdGEgPSBwYXRpZW50LmlkZW50LGNvbC5uYW1lID0gInBhdGllbnQuaWRlbnQiKQphbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkID0gU2V0SWRlbnQoYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCwgdmFsdWUgPSJwYXRpZW50LmlkZW50IikKYWNjX2RlZyA8LSBGaW5kTWFya2VycyhhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkLCBpZGVudC4xID0gIkhNU0MiLGxvZ2ZjLnRocmVzaG9sZCA9IDEuNSxmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMoYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCkpCmVucmljaG1lbnRfYW5hbHlzaXMoYWNjX2RlZyxiYWNrZ3JvdW5kID0gVmFyaWFibGVGZWF0dXJlcyhhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkKSxmZHJfQ3V0b2ZmID0gMC4wMSxpZGVudC4xID0gIkhNU0MiLGlkZW50LjIgPSAiQUNDIixzaG93X2J5ID0gMSkKYGBgCgojIyBNWUIgZXhwcmVzc2lvbgoKYGBge3IgZmlnLndpZHRoPTEwfQphbGxfYWNjX2NhbmNlcl9jZWxscyA9IFNldElkZW50KG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzLHZhbHVlID0gInBhdGllbnQuaWRlbnQiKSAjYWN0aXZlIHNubiBncmFwaApGZWF0dXJlUGxvdChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9ICJNWUIiLGxhYmVsID0gVCkKYGBgCgojIyBDTlYgey50YWJzZXR9CgoKCmBgYHtyfQojc2V0IGNlbGwgdHlwZXMKbmV3LmNsdXN0ZXIuaWRzIDwtIGMoImNhbmNlciIsICMwCiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjMQogICAgICAgICAgICAgICAgICAgICAiQ0FGIiwgIzIKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMzCiAgICAgICAgICAgICAgICAgICAgICJFbmRvdGhlbGlhbCIsICM0CiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjNQogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzYKICAgICAgICAgICAgICAgICAgICAgIkNBRiIsICM3CiAgICAgICAgICAgICAgICAgICAgICJDQUYiLCAjOAogICAgICAgICAgICAgICAgICAgICAiQ0FGIiwgIzkKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxMAogICAgICAgICAgICAgICAgICAgICAiQ0FGIiwgIzExCiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjMTIKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxMwogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzE0CiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjMTUKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxNgogICAgICAgICAgICAgICAgICAgICAiV0JDIiwgIzE3CiAgICAgICAgICAgICAgICAgICAgICJDQUYiICMxOAogICAgICAgICAgICAgICAgICAgICApCmBgYAoKCmBgYHtyIGZpZy5zaG93PSdoaWRlJ30KI3JlbmFtZSBpZGVudHM6CmFjY19hbGxfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2NfYWxsX2NlbGxzLHZhbHVlID0gIlJOQV9zbm5fcmVzLjEiKSAjYWN0aXZlIHNubiBncmFwaApuYW1lcyhuZXcuY2x1c3Rlci5pZHMpIDwtIGxldmVscyhhY2NfYWxsX2NlbGxzKSAjYWRkIHNubiBncmFwaCBsZXZlbHMgdG8gbmV3LmNsdXN0ZXIuaWRzCmFjY19hbGxfY2VsbHNAbWV0YS5kYXRhW1sic2V1cmF0X2NsdXN0ZXJzIl1dID0gYWNjX2FsbF9jZWxsc0BtZXRhLmRhdGFbWyJSTkFfc25uX3Jlcy4xIl1dCmFjY19hbGxfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2NfYWxsX2NlbGxzLHZhbHVlID0gInNldXJhdF9jbHVzdGVycyIpCmFjY19hbGxfY2VsbHMgPC0gUmVuYW1lSWRlbnRzKGFjY19hbGxfY2VsbHMsIG5ldy5jbHVzdGVyLmlkcykgCgojIGRpdmlkZSAiY2FuY2VyIiBpbnRvIHBhdGllbnRzOgpjZWxsX3R5cGVzID0gYWNjX2FsbF9jZWxsc0BhY3RpdmUuaWRlbnQgJT4lIGFzLmRhdGEuZnJhbWUoKQpjZWxsX3R5cGVzWywxXTwtIGFzLmNoYXJhY3RlcihjZWxsX3R5cGVzWywxXSkKY2VsbF90eXBlcyA9IGNiaW5kKGNlbGxfdHlwZXMsYWNjX2FsbF9jZWxscyRwYXRpZW50LmlkZW50KSAlPiUgc2V0bmFtZXMob2xkID0gbmFtZXMoLiksIAogICAgICAgICBuZXcgPSBjKCdjZWxsX3R5cGUnLCdwYXRpZW50JykpCmNlbGxfdHlwZXNbY2VsbF90eXBlcyRjZWxsX3R5cGUgPT0gImNhbmNlciIsXSA9IGNlbGxfdHlwZXNbY2VsbF90eXBlcyRjZWxsX3R5cGUgPT0gImNhbmNlciIsMl0KCgojIGhtc2Nfcm93cyA9IChzdGFydHNXaXRoKHggPSByb3duYW1lcyhjZWxsX3R5cGVzKSxwcmVmaXggPSAiQUNDLnBsYXRlMiIpIHwgc3RhcnRzV2l0aCh4ID0gcm93bmFtZXMoY2VsbF90eXBlcykscHJlZml4ID0gIkFDQzEuIikpICYgY2VsbF90eXBlc1ssMV0gPT0gImNhbmNlciIgCiMgYWNjX3Jvd3MgPSAhKHN0YXJ0c1dpdGgoeCA9IHJvd25hbWVzKGNlbGxfdHlwZXMpLHByZWZpeCA9ICJBQ0MucGxhdGUyIikgfCBzdGFydHNXaXRoKHggPSByb3duYW1lcyhjZWxsX3R5cGVzKSxwcmVmaXggPSAiQUNDMS4iKSkgJiBjZWxsX3R5cGVzWywxXSA9PSAiY2FuY2VyIiAKIyBjZWxsX3R5cGVzWywxXVtobXNjX3Jvd3NdICA9ICJITVNDIgojIGNlbGxfdHlwZXNbLDFdW2FjY19yb3dzXSAgPSAiQUNDIgoKI2FkZCB0byBtZXRhZGF0YToKY2VsbF90eXBlc1ssMl0gPSBOVUxMIApjZWxsX3R5cGVzW2NlbGxfdHlwZXMkY2VsbF90eXBlID09ICJBQ0MxIixdID0gIkhNU0MiCmFjY19hbGxfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPWFjY19hbGxfY2VsbHMgLG1ldGFkYXRhID0gY2VsbF90eXBlcyxjb2wubmFtZSA9ICJjZWxsLnR5cGUiKQpgYGAKIyMjIENOViBVTUFQIAoKYGBge3IgZmlnLndpZHRoPTEwfQpsaWJyYXJ5KGluZmVyY252KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYWNjX2Fubm90YXRpb24gID0gYXMuZGF0YS5mcmFtZShhY2NfYWxsX2NlbGxzQG1ldGEuZGF0YVssImNlbGwudHlwZSIsZHJvcCA9IEZdKQphY2NfYW5ub3RhdGlvbiA9IGFjY19hbm5vdGF0aW9uICU+JSByb3duYW1lc190b19jb2x1bW4oIm9yaWcuaWRlbnQiKSAKYWNjX2Fubm90YXRpb24gPSBhY2NfYW5ub3RhdGlvbiAlPiUgbXV0YXRlKG9yaWcuaWRlbnQgPSBnc3ViKHggPSBhY2NfYW5ub3RhdGlvbiRvcmlnLmlkZW50LHBhdHRlcm4gPSAiXFwuIiwgcmVwbGFjZW1lbnQgPSAiLSIpICU+JSAKICBnc3ViKHBhdHRlcm4gPSAiXyIsIHJlcGxhY2VtZW50ID0gIi0iKSkKICAKCndyaXRlLnRhYmxlKGFjY19hbm5vdGF0aW9uLCAiLi9EYXRhL2luZmVyQ05WL2FjY19hbm5vdGF0aW9uLnR4dCIsIGFwcGVuZCA9IEZBTFNFLCAKICAgICAgICAgICAgc2VwID0gIlx0IiwgZGVjID0gIi4iLHJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGKQoKaW5mZXJjbnZfb2JqID0gQ3JlYXRlSW5mZXJjbnZPYmplY3QocmF3X2NvdW50c19tYXRyaXg9Ii4vRGF0YS9pbmZlckNOVi9hbGwuNGljbnYudHh0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25zX2ZpbGU9Ii4vRGF0YS9pbmZlckNOVi9hY2NfYW5ub3RhdGlvbi50eHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWxpbT0iXHQiLGdlbmVfb3JkZXJfZmlsZT0iLi9EYXRhL2luZmVyQ05WL2dlbmNvZGVfdjE5X2dlbmVfcG9zLnR4dCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHJlZl9ncm91cF9uYW1lcz1jKCJDQUYiLCAiRW5kb3RoZWxpYWwiLCAiV0JDIikpICNncm91cHMgb2Ygbm9ybWFsIGNlbGxzCgppbmZlcmNudl9vYmpfZGVmYXVsdCA9IGluZmVyY252OjpydW4oaW5mZXJjbnZfb2JqLCBjdXRvZmY9MSwgb3V0X2Rpcj0nLi9EYXRhL2luZmVyQ05WL2luZmVyY252X291dHB1dCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2J5X2dyb3Vwcz1ULCBwbG90X3N0ZXBzPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVub2lzZT1UUlVFLCBITU09RkFMU0UsIG5vX3ByZWxpbV9wbG90PVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbmdfcmVzPTMwMCkKcGxvdF9jbnYoaW5mZXJjbnZfb2JqX2RlZmF1bHQsIG91dHB1dF9mb3JtYXQgPSAicG5nIiwgIHdyaXRlX2V4cHJfbWF0cml4ID0gRkFMU0Usb3V0X2RpciA9ICIuL0RhdGEvaW5mZXJDTlYvIixwbmdfcmVzCT04MDAsb2JzX3RpdGxlID0gIk1hbGlnbmFudCBjZWxscyIscmVmX3RpdGxlID0gIk5vcm1hbCBjZWxscyIpCgoKY2x1c3Rlci5pbmZvPUZldGNoRGF0YShhY2NfYWxsX2NlbGxzLGMoImlkZW50Iiwib3JpZy5pZGVudCIsIlVNQVBfMSIsIlVNQVBfMiIsIm5Db3VudF9STkEiLCJuRmVhdHVyZV9STkEiLCJwZXJjZW50Lm10IiwicGF0aWVudC5pZGVudCIsInNldXJhdF9jbHVzdGVycyIpKQpjbHVzdGVyLmluZm8kY2VsbD1yb3duYW1lcyhjbHVzdGVyLmluZm8pCgpsaWJyYXJ5KGxpbW1hKQpzbW9vdGhlZD1hcHBseShpbmZlcmNudl9vYmpfZGVmYXVsdEBleHByLmRhdGEsMix0cmljdWJlTW92aW5nQXZlcmFnZSwgc3Bhbj0wLjAxKQpjbnNpZz1zcXJ0KGFwcGx5KChzbW9vdGhlZC0xKV4yLDIsbWVhbikpCnVtYXA9Y2x1c3Rlci5pbmZvCm5hbWVzKGNuc2lnKT11bWFwJGNlbGwKCmFjY19hbGxfY2VsbHMgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjX2FsbF9jZWxscywgbWV0YWRhdGEgPSBjbnNpZywgY29sLm5hbWUgPSAiY29weW51bWJlciIpCmFjY19hbGxfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2NfYWxsX2NlbGxzLHZhbHVlID0gImNlbGwudHlwZSIpCkZlYXR1cmVQbG90KGFjY19hbGxfY2VsbHMsICJjb3B5bnVtYmVyIixwdC5zaXplID0gMSwgY29scyA9IGMoImxpZ2h0Ymx1ZSIsIm9yYW5nZSIsInJlZCIsImRhcmtyZWQiKSxsYWJlbCA9IFQscmVwZWwgPSBUKQpgYGAKCgojIyMgQ05WIHBsb3QgCgohW0NOViBwbG90XSgvc2NpL2xhYnMveW90YW1kL2xhYl9zaGFyZS9hdmlzaGFpLndpemVsL1JfcHJvamVjdHMvSE1TQy9EYXRhL2luZmVyQ05WL2luZmVyY252LnBuZykKIyMgey19CgojIyBDTlYgc3VidHlwZXMKCmBgYHtyfQpjbnZfc3VidHlwZXMgPSBhcy5kYXRhLmZyYW1lKGN1dHJlZShpbmZlcmNudl9vYmpfZGVmYXVsdEB0dW1vcl9zdWJjbHVzdGVyc1tbImhjIl1dW1siSE1TQyJdXSwgayA9IDIpKQpuYW1lcyhjbnZfc3VidHlwZXMpWzFdID0gImNudi5jbHVzdGVyIgpyb3duYW1lcyhjbnZfc3VidHlwZXMpID0gcm93bmFtZXMoY252X3N1YnR5cGVzKSAlPiUgZ3N1YihwYXR0ZXJuID0gIi0iLHJlcGxhY2VtZW50ID0gIlxcLiIpCmluZmVyY252Lm9ic2VydmF0aW9ucyA9IGRhdGEuZnJhbWUoZnJlYWQoZmlsZSA9ICIuL0RhdGEvaW5mZXJDTlYvaW5mZXJjbnYub2JzZXJ2YXRpb25zLnR4dCIpLCByb3cubmFtZXM9MSkKbmFtZXNfdG9fa2VlcCA9IGNvbG5hbWVzKGluZmVyY252Lm9ic2VydmF0aW9ucykgJWluJSAoY29sbmFtZXMoYWNjMV9jYW5jZXJfY2VsbHMpICU+JSBnc3ViKHBhdHRlcm4gPSAiXyIscmVwbGFjZW1lbnQgPSAiXFwuIikpCmluZmVyY252Lm9ic2VydmF0aW9ucyA9IGluZmVyY252Lm9ic2VydmF0aW9uc1ssbmFtZXNfdG9fa2VlcF0KYGBgCgpgYGB7cn0Kcm90YXRlIDwtIGZ1bmN0aW9uKHgpIHQoYXBwbHkoeCwgMiwgcmV2KSkKaW5mZXJjbnYub2JzZXJ2YXRpb25zMiA9IGluZmVyY252Lm9ic2VydmF0aW9ucyAlPiUgcm90YXRlKCkgJT4lICByb3RhdGUoKSAlPiUgcm90YXRlKCklPiUgYXMuZGF0YS5mcmFtZSgpIApicmVha3MgPSBjKDAuNzAwODkxODYxNzA0ODU3LAowLjc0MjM2Njk0NTUyODM2OSwKMC43ODM4NDIwMjkzNTE4ODEsCjAuODI1MzE3MTEzMTc1MzkzLAowLjg2Njc5MjE5Njk5ODkwNSwKMC45MDgyNjcyODA4MjI0MTcsCjAuOTQ5NzQyMzY0NjQ1OTI4LAowLjk5MTIxNzQ0ODQ2OTQ0LAoxLjAzMjY5MjUzMjI5Mjk1LAoxLjA3NDE2NzYxNjExNjQ2LAoxLjExNTY0MjY5OTkzOTk4LAoxLjE1NzExNzc4Mzc2MzQ5LAoxLjE5ODU5Mjg2NzU4NywKMS4yNDAwNjc5NTE0MTA1MSwKMS4yODE1NDMwMzUyMzQwMiwKMS4zMjMwMTgxMTkwNTc1MykKcGhlYXRtYXAoaW5mZXJjbnYub2JzZXJ2YXRpb25zMixjbHVzdGVyX2NvbHMgPSBGLGNsdXN0ZXJfcm93cyA9IEYsIHNob3dfcm93bmFtZXMgPSBGLHNob3dfY29sbmFtZXMgPSBGLCBicmVha3MgPSBicmVha3MsY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKHJldihjKCJkYXJrcmVkIiwgIndoaXRlIiwgImRhcmtibHVlIikpKSgxNSksYW5ub3RhdGlvbl9yb3cgPSBjbnZfc3VidHlwZXMpCgpgYGAKCmBgYHtyfQpyb3duYW1lcyhjbnZfc3VidHlwZXMpID0gcm93bmFtZXMoY252X3N1YnR5cGVzKSAlPiUgZ3N1YihwYXR0ZXJuID0gIjJcXC4iLHJlcGxhY2VtZW50ID0gIjJfIikKcm93bmFtZXMoY252X3N1YnR5cGVzKSA9IHJvd25hbWVzKGNudl9zdWJ0eXBlcykgJT4lIGdzdWIocGF0dGVybiA9ICIzXFwuIixyZXBsYWNlbWVudCA9ICIzXyIpCmFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBjbnZfc3VidHlwZXMpCmBgYAoKYGBge3J9CkRpbVBsb3QoYWNjMV9jYW5jZXJfY2VsbHMsZ3JvdXAuYnkgPSAiY252LmNsdXN0ZXIiLHB0LnNpemUgPSAyLGNvbHMgPWNvbG9ycykKYGBgCgoKIyMgT3JpZ2luYWwgc2NvcmUKYGBge3J9Cm9yaWdpbmFsX215b19nZW5lcyA9IGMoICJUUDYzIiwgIlRQNzMiLCAiQ0FWMSIsICJDREgzIiwgIktSVDUiLCAiS1JUMTQiLCAiQUNUQTIiLCAiVEFHTE4iLCAiTVlMSyIsICJES0szIikKb3JpZ2luYWxfbHVtX2dlbmVzID0gYygiS0lUIiwgIkVIRiIsICJFTEY1IiwgIktSVDciLCAiQ0xETjMiLCAiQ0xETjQiLCAiQ0QyNCIsICJMR0FMUzMiLCAiTENOMiIsICJTTFBJIiApCmBgYAoKCgpgYGB7cn0KY2FsY3VsYXRlX3Njb3JlKGRhdGFzZXQgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyxteW9fZ2VuZXMgPSBvcmlnaW5hbF9teW9fZ2VuZXMsbHVtX2dlbmVzID0gb3JpZ2luYWxfbHVtX2dlbmVzKQpgYGAKIyMgT3JpZ2luYWwgc2NvcmUgb2YgQUNDMQpgYGB7cn0KY2FsY3VsYXRlX3Njb3JlKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscyxteW9fZ2VuZXMgPSBvcmlnaW5hbF9teW9fZ2VuZXMsbHVtX2dlbmVzID0gb3JpZ2luYWxfbHVtX2dlbmVzLGx1bV90aHJlc2hvbGQgPSAwLG15b190aHJlc2hvbGQgPSAwKQpgYGAKCgojIyAwLjM1IE1vc3QgY29ycmVsYXRlZCBzY29yZSB7LnRhYnNldH0KCiMjIyBNeW8gZ2VuZXMKCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBjb2xsYXBzZT1UfQpteW9fcHJvdGVpbl9tYXJrZXJzID0gYygiQ05OMSIsICJUUDYzIiwiQUNUQTIiKQp0b3BfbXlvICA9IHRvcF9jb3JyZWxhdGVkKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscywgZ2VuZXMgPSBteW9fcHJvdGVpbl9tYXJrZXJzLHRocmVzaG9sZCA9IDAuMzUpCnByaW50KCJOdW1iZXIgb2YgZ2VuZXMgPSAiICU+JSBwYXN0ZShsZW5ndGgodG9wX215bykpKQptZXNzYWdlKCJOYW1lcyBvZiBnZW5lczoiKQp0b3BfbXlvICU+JSBoZWFkKDMwKQptZXNzYWdlKCJHZW5lcyB0aGF0IGFsc28gYXBlYXJlZCBpbiB0aGUgb3JpZ2luYWwgc2NvcmU6IikKYmFzZTo6aW50ZXJzZWN0KHRvcF9teW8sb3JpZ2luYWxfbXlvX2dlbmVzKSAKYGBgCmBgYHtyfQpteW9fZW5yaWNoX3JlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wX215byxiYWNrZ3JvdW5kID0gcm93bmFtZXMoYWNjMV9jYW5jZXJfY2VsbHMpLGhvbWVyID0gVCx0aXRsZSA9ICJteW8gdG9wIGVucmljaG1lbnQiLGN1c3RvbV9wYXRod2F5cyA9IGx1bWluYWxfZ3MpCm15b19lbnJpY2hfcmVzCmBgYAojIyMgTHVtIGdlbmVzCmBgYHtyfQpsdW1fcHJvdGVpbl9tYXJrZXJzID0gYygiS0lUIikKdG9wX2x1bSAgPSB0b3BfY29ycmVsYXRlZChkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsIGdlbmVzID0gbHVtX3Byb3RlaW5fbWFya2Vycyx0aHJlc2hvbGQgPSAwLjM1LG5fdmFyZ2VuZXMgPSA1MDAwKQpwcmludCgiTnVtYmVyIG9mIGdlbmVzID0gIiAlPiUgcGFzdGUobGVuZ3RoKHRvcF9sdW0pKSkKbWVzc2FnZSgiTmFtZXMgb2YgZ2VuZXM6IikKdG9wX2x1bSAlPiUgaGVhZCgzMCkKbWVzc2FnZSgiR2VuZXMgdGhhdCBhbHNvIGFwZWFyZWQgaW4gdGhlIG9yaWdpbmFsIHNjb3JlOiIpCmJhc2U6OmludGVyc2VjdCh0b3BfbHVtLG9yaWdpbmFsX2x1bV9nZW5lcykgCmBgYAoKYGBge3J9Cmx1bV9lbnJpY2hfcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3BfbHVtLGJhY2tncm91bmQgPSByb3duYW1lcyhhY2MxX2NhbmNlcl9jZWxscyksaG9tZXIgPSBULHRpdGxlID0gImx1bSB0b3AgZW5yaWNobWVudCIsY3VzdG9tX3BhdGh3YXlzID0gbHVtaW5hbF9ncykKbHVtX2VucmljaF9yZXMKYGBgCiMjIyB0b3AgY29ycmVsYXRlZCBzY29yZQpgYGB7cn0KY2FsY3VsYXRlX3Njb3JlKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscyxteW9fZ2VuZXMgPSB0b3BfbXlvLGx1bV9nZW5lcyA9IHRvcF9sdW0sbHVtX3RocmVzaG9sZCA9IDAsbXlvX3RocmVzaG9sZCA9IDApCmBgYAoKCiMjIyAgZW5yaWNoZWQgZ2VuZXMgc2NvcmUKYGBge3J9CnJvd25hbWVzKGx1bV9lbnJpY2hfcmVzKSA9IGx1bV9lbnJpY2hfcmVzJHBhdGh3YXlfbmFtZQpsdW1fZW5yaWNoZWRfZ2VuZXMgPSBsdW1fZW5yaWNoX3Jlc1sxLCJnZW5lSUQiXSAlPiUgc3Ryc3BsaXQoc3BsaXQgPSAiLyIpICU+JSAuW1sxXV0gJT4lIGMoLixsdW1fcHJvdGVpbl9tYXJrZXJzKSAjYWRkIG9yaWdpbmFsIG1hcmtlcnMKYGBgCgpgYGB7cn0Kcm93bmFtZXMobXlvX2VucmljaF9yZXMpID0gbXlvX2VucmljaF9yZXMkcGF0aHdheV9uYW1lCm15b19lbnJpY2hlZF9nZW5lcyA9IG15b19lbnJpY2hfcmVzWzEsImdlbmVJRCJdICU+JSBzdHJzcGxpdChzcGxpdCA9ICIvIikgJT4lIC5bWzFdXSAlPiUgYyguLG15b19wcm90ZWluX21hcmtlcnMpICNhZGQgb3JpZ2luYWwgbWFya2VycwpgYGAKCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG15b19lbnJpY2hlZF9nZW5lcyxsdW1fZ2VuZXMgPSBsdW1fZW5yaWNoZWRfZ2VuZXMsbHVtX3RocmVzaG9sZCA9IC0yLjUsbXlvX3RocmVzaG9sZCA9IC0yLjUpCmBgYAoKCiMjIHstfQoKCiMjICAwLjIgTW9zdCBjb3JyZWxhdGVkIHNjb3JlIHsudGFic2V0fQoKIyMjICBteW8gR2VuZXMKCmBgYHtyfQoKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpuX3ZhcmdlbmVzID0gMjAwMApteW9fcHJvdGVpbl9tYXJrZXJzID0gYygiQ05OMSIsICJUUDYzIiwiQUNUQTIiKQp0b3BfbXlvICA9IHRvcF9jb3JyZWxhdGVkKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscywgZ2VuZXMgPSBteW9fcHJvdGVpbl9tYXJrZXJzLHRocmVzaG9sZCA9IDAuMixuX3ZhcmdlbmVzID0gbl92YXJnZW5lcykKcHJpbnQoIk51bWJlciBvZiBnZW5lcyA9ICIgJT4lIHBhc3RlKGxlbmd0aCh0b3BfbXlvKSkpCm1lc3NhZ2UoIk5hbWVzIG9mIGdlbmVzOiIpCnRvcF9teW8gJT4lIGhlYWQoMzApCm1lc3NhZ2UoIkdlbmVzIHRoYXQgYWxzbyBhcGVhcmVkIGluIHRoZSBvcmlnaW5hbCBzY29yZToiKQpiYXNlOjppbnRlcnNlY3QodG9wX215byxvcmlnaW5hbF9teW9fZ2VuZXMpIApteW9fZW5yaWNoX3JlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wX215byxiYWNrZ3JvdW5kID0gVmFyaWFibGVGZWF0dXJlcyhhY2MxX2NhbmNlcl9jZWxscykgJT4lIGhlYWQobl92YXJnZW5lcyksaG9tZXIgPSBULHRpdGxlID0gIm15byB0b3AgZW5yaWNobWVudCIsY3VzdG9tX3BhdGh3YXlzID0gbHVtaW5hbF9ncykKbXlvX2VucmljaF9yZXMKYGBgCgojIyMgIEx1bSBHZW5lcwoKYGBge3J9CmFjYzFfY2FuY2VyX2NlbGxzID0gRmluZFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbmZlYXR1cmVzID0gMTUwMDApCmBgYAoKYGBge3J9Cmx1bV9wcm90ZWluX21hcmtlcnMgPSBjKCJLSVQiKQpuX3ZhcmdlbmVzID0gMjAwMAp0b3BfbHVtICA9IHRvcF9jb3JyZWxhdGVkKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscywgZ2VuZXMgPSBsdW1fcHJvdGVpbl9tYXJrZXJzLHRocmVzaG9sZCA9IDAuMyxuX3ZhcmdlbmVzID0gbl92YXJnZW5lcykKcHJpbnQoIk51bWJlciBvZiBnZW5lcyA9ICIgJT4lIHBhc3RlKGxlbmd0aCh0b3BfbHVtKSkpCm1lc3NhZ2UoIk5hbWVzIG9mIGdlbmVzOiIpCnRvcF9sdW0gJT4lIGhlYWQoMzApCm1lc3NhZ2UoIkdlbmVzIHRoYXQgYWxzbyBhcGVhcmVkIGluIHRoZSBvcmlnaW5hbCBzY29yZToiKQpiYXNlOjppbnRlcnNlY3QodG9wX2x1bSxvcmlnaW5hbF9sdW1fZ2VuZXMpIAoKbHVtX2VucmljaF9yZXMgPSBnZW5lc192ZWNfZW5yaWNobWVudChnZW5lcyA9IHRvcF9sdW0sYmFja2dyb3VuZCA9IFZhcmlhYmxlRmVhdHVyZXMoYWNjMV9jYW5jZXJfY2VsbHMpICU+JSBoZWFkKG5fdmFyZ2VuZXMpLGhvbWVyID0gVCx0aXRsZSA9ICJsdW0gdG9wIGVucmljaG1lbnQiLGN1c3RvbV9wYXRod2F5cyA9IGx1bWluYWxfZ3MpCmx1bV9lbnJpY2hfcmVzCmNhbGN1bGF0ZV9zY29yZShkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbXlvX2dlbmVzID0gdG9wX215byxsdW1fZ2VuZXMgPSB0b3BfbHVtLGx1bV90aHJlc2hvbGQgPSAyLG15b190aHJlc2hvbGQgPSAxKQpgYGAKCgpgYGB7cn0KbXlvX2ludGVyc2VjdGVkID0gaW50ZXJzZWN0KHRvcF9teW8sb3JpZ2luYWxfbXlvX2dlbmVzKSAKbHVtX2ludGVyc2VjdGVkID0gaW50ZXJzZWN0KHRvcF9sdW0sb3JpZ2luYWxfbHVtX2dlbmVzKSAKbWVzc2FnZSgiZ2VuZXMgaW4gbXlvIHNjb3JlOiIpCm15b19pbnRlcnNlY3RlZAoKbWVzc2FnZSgiZ2VuZXMgaW4gbHVtIHNjb3JlOiIpCmx1bV9pbnRlcnNlY3RlZApjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG15b19pbnRlcnNlY3RlZCxsdW1fZ2VuZXMgPSBsdW1faW50ZXJzZWN0ZWQsbHVtX3RocmVzaG9sZCA9IDIsbXlvX3RocmVzaG9sZCA9IDEpCmBgYAoKCgojIyMgZW5yaWNoZWQgZ2VuZXMKYGBge3J9CnJvd25hbWVzKGx1bV9lbnJpY2hfcmVzKSA9IGx1bV9lbnJpY2hfcmVzJHBhdGh3YXlfbmFtZQpsdW1fZW5yaWNoZWRfZ2VuZXMgPSBsdW1fZW5yaWNoX3Jlc1szLCJnZW5lSUQiXSAlPiUgc3Ryc3BsaXQoc3BsaXQgPSAiLyIpICU+JSAuW1sxXV0gJT4lIGMoLixsdW1fcHJvdGVpbl9tYXJrZXJzKSAjYWRkIG9yaWdpbmFsIG1hcmtlcnMKYGBgCgpgYGB7cn0Kcm93bmFtZXMobXlvX2VucmljaF9yZXMpID0gbXlvX2VucmljaF9yZXMkcGF0aHdheV9uYW1lCm15b19lbnJpY2hlZF9nZW5lcyA9IG15b19lbnJpY2hfcmVzWzMsImdlbmVJRCJdICU+JSBzdHJzcGxpdChzcGxpdCA9ICIvIikgJT4lIC5bWzFdXSAlPiUgYyguLG15b19wcm90ZWluX21hcmtlcnMpICNhZGQgb3JpZ2luYWwgbWFya2VycwpgYGAKCmBgYHtyfQptZXNzYWdlKCJnZW5lcyBpbiBteW8gc2NvcmU6IikKbXlvX2VucmljaGVkX2dlbmVzCgptZXNzYWdlKCJnZW5lcyBpbiBsdW0gc2NvcmU6IikKbHVtX2VucmljaGVkX2dlbmVzCgpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG15b19lbnJpY2hlZF9nZW5lcyxsdW1fZ2VuZXMgPSBsdW1fZW5yaWNoZWRfZ2VuZXMsbHVtX3RocmVzaG9sZCA9IDAsbXlvX3RocmVzaG9sZCA9IC0xKQpgYGAKCgojIyMgZW5yaWNoZWQgZ2VuZXMgYW5kIGluIG9yaWdpbmFsIHNjb3JlCmBgYHtyfQpteW9fZW5yaWNoZWRfZ2VuZXMgPSBteW9fZW5yaWNoZWRfZ2VuZXNbbXlvX2VucmljaGVkX2dlbmVzICVpbiUgb3JpZ2luYWxfbXlvX2dlbmVzXQpsdW1fZW5yaWNoZWRfZ2VuZXMgPSBsdW1fZW5yaWNoZWRfZ2VuZXNbbHVtX2VucmljaGVkX2dlbmVzICVpbiUgb3JpZ2luYWxfbHVtX2dlbmVzXQoKbWVzc2FnZSgiZ2VuZXMgaW4gbXlvIHNjb3JlOiIpCm15b19lbnJpY2hlZF9nZW5lcwoKbWVzc2FnZSgiZ2VuZXMgaW4gbHVtIHNjb3JlOiIpCmx1bV9lbnJpY2hlZF9nZW5lcwoKY2FsY3VsYXRlX3Njb3JlKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscyxteW9fZ2VuZXMgPSBteW9fZW5yaWNoZWRfZ2VuZXMsbHVtX2dlbmVzID0gbHVtX2VucmljaGVkX2dlbmVzLGx1bV90aHJlc2hvbGQgPSAyLG15b190aHJlc2hvbGQgPSAyKQpgYGAKCiMjIHstfQoKCiMgSFBWCgpPbmx5IEhNU0MgY2FuY2VyIGNlbGxzOgoKYGBge3J9CkhQVjMzX1AzID0gZnJlYWQoIi4vRGF0YS9IUFYzM19QMy50eHQiLGNvbC5uYW1lcyA9IGMoInBsYXRlIiwicmVhZHMiKSkgJT4lIGFzLmRhdGEuZnJhbWUoKQpIUFYzM19QMy5kZiA9IEhQVjMzX1AzICU+JSBtdXRhdGUoCiAgcGxhdGUgPSBnc3ViKHggPUhQVjMzX1AzJHBsYXRlLCByZXBsYWNlbWVudCA9ICIiLHBhdHRlcm4gPSAiXy4qJCIpIAogICU+JSBnc3ViKHBhdHRlcm4gPSAiLVAiLHJlcGxhY2VtZW50ID0gIi5QIikgCiAgJT4lIGdzdWIocGF0dGVybiA9ICItIixyZXBsYWNlbWVudCA9ICJfIiwpCiAgKQpIUFYzM19QMy5kZiA9IEhQVjMzX1AzLmRmICU+JSBkcGx5cjo6ZmlsdGVyKEhQVjMzX1AzLmRmJHBsYXRlICVpbiUgY29sbmFtZXMoYWNjMV9jYW5jZXJfY2VsbHMpKQpyb3duYW1lcyhIUFYzM19QMy5kZikgIDwtIEhQVjMzX1AzLmRmJHBsYXRlCkhQVjMzX1AzLmRmJHBsYXRlID0gTlVMTAoKCkhQVjMzX1AyID0gZnJlYWQoIi4vRGF0YS9IUFYzM19QMi50eHQiLGNvbC5uYW1lcyA9IGMoInBsYXRlIiwicmVhZHMiKSkgJT4lIGFzLmRhdGEuZnJhbWUoKQpIUFYzM19QMi5kZiA9IEhQVjMzX1AyICU+JSBtdXRhdGUoCiAgcGxhdGUgPSBnc3ViKHggPUhQVjMzX1AyJHBsYXRlLCByZXBsYWNlbWVudCA9ICIiLHBhdHRlcm4gPSAiXy4qJCIpIAogICU+JSBnc3ViKHBhdHRlcm4gPSAicGxhdGUyLSIscmVwbGFjZW1lbnQgPSAicGxhdGUyXyIsKQogICU+JSBnc3ViKHBhdHRlcm4gPSAiLSIscmVwbGFjZW1lbnQgPSAiXFwuIiwpCiAgKQpIUFYzM19QMi5kZiA9IEhQVjMzX1AyLmRmICU+JSBkcGx5cjo6ZmlsdGVyKEhQVjMzX1AyLmRmJHBsYXRlICVpbiUgY29sbmFtZXMoYWNjMV9jYW5jZXJfY2VsbHMpKQpyb3duYW1lcyhIUFYzM19QMi5kZikgIDwtIEhQVjMzX1AyLmRmJHBsYXRlCkhQVjMzX1AyLmRmJHBsYXRlID0gTlVMTAoKSFBWMzMgPSByYmluZChIUFYzM19QMy5kZixIUFYzM19QMi5kZikKYWNjMV9jYW5jZXJfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxtZXRhZGF0YSA9IEhQVjMzLGNvbC5uYW1lID0gIkhQVjMzLnJlYWRzIikKRmVhdHVyZVBsb3QoYWNjMV9jYW5jZXJfY2VsbHMsZmVhdHVyZXMgPSAiSFBWMzMucmVhZHMiLG1heC5jdXRvZmYgPSA0MCkKYGBgCmBgYHtyfQpkYXRhID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSAiSFBWMzMucmVhZHMiKQpwcmludCgKICBkYXRhICU+JSAKICBnZ3Bsb3QoYWVzKCB4PUhQVjMzLnJlYWRzKSkgKyAKICBnZW9tX2RlbnNpdHkoKQopCmBgYApgYGB7cn0KaHB2MzNfcG9zaXRpdmUgPSBIUFYzMyAlPiUgZHBseXI6Om11dGF0ZShocHYzM19wb3NpdGl2ZSA9IGNhc2Vfd2hlbihyZWFkcyA+PSA3NTAgfiJzdHJvbmcgcG9zaXRpdmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRzIDwgNzUwICYgcmVhZHMgPiAxMCB+ICJwb3NpdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZHMgPCAxMCB+ICJuZWdhdGl2ZSIpCikKCgoKaHB2MzNfcG9zaXRpdmUkcmVhZHMgPSBOVUxMCmFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBocHYzM19wb3NpdGl2ZSkKYGBgCgpgYGB7cn0KRGltUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxncm91cC5ieSAgPSBjKCJocHYzM19wb3NpdGl2ZSIpLHB0LnNpemUgPSAyKQpgYGAKCgojIGNOTUYKYGBge3J9CmxpYnJhcnkocmV0aWN1bGF0ZSkKYGBgCgpgYGB7cn0KI3dyaXRlIGV4cHJlc3Npb24KYWNjMV9jYW5jZXJfY2VsbHMgPSBGaW5kVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxuZmVhdHVyZXMgPSAyMDAwKQp2YXJnZW5lcyA9IFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMpCmhtc2NfZXhwcmVzc2lvbiA9IHQoYXMubWF0cml4KEdldEFzc2F5RGF0YShhY2MxX2NhbmNlcl9jZWxscyxzbG90PSdkYXRhJykpKQpobXNjX2V4cHJlc3Npb24gPSAyKipobXNjX2V4cHJlc3Npb24gI2NvbnZlcnQgZnJvbSBsb2cyKHRwbSsxKSB0byB0cG0KaG1zY19leHByZXNzaW9uID0gaG1zY19leHByZXNzaW9uLTEKIyBobXNjX2V4cHJlc3Npb24gPSBobXNjX2V4cHJlc3Npb25bLCFjb2xTdW1zKGhtc2NfZXhwcmVzc2lvbj09MCwgbmEucm09VFJVRSk9PW5yb3coaG1zY19leHByZXNzaW9uKV0gI2RlbGV0ZSByb3dzIHRoYXQgaGF2ZSBhbGwgMApobXNjX2V4cHJlc3Npb24gPSBobXNjX2V4cHJlc3Npb25bLHZhcmdlbmVzXQp3cml0ZS50YWJsZSh4ID0gaG1zY19leHByZXNzaW9uICxmaWxlID0gJy4vRGF0YS9jTk1GL2htc2NfZXhwcmVzc2lvbkRhdGFfMkt2YXJnZW5lcy50eHQnLHNlcCA9ICJcdCIpCmBgYAoKCgpgYGB7cHl0aG9uIGV2YWw9Rn0KZnJvbSBjbm1mIGltcG9ydCBjTk1GCm5hbWUgPSAnSE1TQ19jTk1GXzJLdmFyZ2VuZXMnCm91dGRpciA9ICcuL0RhdGEvY05NRicKS19yYW5nZSA9IG5wLmFyYW5nZSgzLDEwKQpjbm1mX29iaiA9IGNOTUYob3V0cHV0X2Rpcj1vdXRkaXIsIG5hbWU9bmFtZSkKY291bnRzX2ZuPScuL0RhdGEvY05NRi9obXNjX2V4cHJlc3Npb25EYXRhXzJLdmFyZ2VuZXMudHh0Jwp0cG1fZm4gPSBjb3VudHNfZm4gIyMgVGhpcyBpcyBhIHdlaXJkIGNhc2Ugd2hlcmUgYmVjYXVzZSB0aGlzIGRhdGFzZXQgaXMgbm90IDMnIGVuZCB1bWkgc2VxdWVuY2luZywgd2Ugb3B0ZWQgdG8gdXNlIHRoZSBUUE0gbWF0cml4IGFzIHRoZSBpbnB1dCBtYXRyaXggcmF0aGVyIHRoYW4gdGhlIGNvdW50IG1hdHJpeAoKY25tZl9vYmoucHJlcGFyZShjb3VudHNfZm49Y291bnRzX2ZuLCBjb21wb25lbnRzPUtfcmFuZ2UsIHNlZWQ9MTQsdHBtX2ZuPXRwbV9mbikKYGBgCgpgYGB7cHl0aG9uIGV2YWw9Rn0KY25tZl9vYmouZmFjdG9yaXplKHdvcmtlcl9pPTAsIHRvdGFsX3dvcmtlcnM9MSkKYGBgCgpgYGB7cHl0aG9uIGV2YWw9Rn0KY25tZl9vYmouY29tYmluZSgpCmNubWZfb2JqLmtfc2VsZWN0aW9uX3Bsb3QoKQpgYGAKIyMgU2F2ZSBvYmplY3QKYGBge3B5dGhvbiBldmFsPUZ9CmltcG9ydCBwaWNrbGUKZiA9IG9wZW4oJy4vRGF0YS9jTk1GL0hNU0NfY05NRl8yS3ZhcmdlbmVzL2NubWZfb2JqLnBja2wnLCAnd2InKQpwaWNrbGUuZHVtcChjbm1mX29iaiwgZikKZi5jbG9zZSgpCmBgYAoKCiMjIExvYWQgb2JqZWN0CmBgYHtweXRob259CmZyb20gY25tZiBpbXBvcnQgY05NRgppbXBvcnQgcGlja2xlCmYgPSBvcGVuKCcuL0RhdGEvY05NRi9ITVNDX2NOTUZfMkt2YXJnZW5lcy9jbm1mX29iai5wY2tsJywgJ3JiJykKY25tZl9vYmogPSBwaWNrbGUubG9hZChmKQpmLmNsb3NlKCkKYGBgCgoKYGBge3B5dGhvbn0Kc2VsZWN0ZWRfayA9IDQKZGVuc2l0eV90aHJlc2hvbGQgPSAwLjEKY25tZl9vYmouY29uc2Vuc3VzKGs9c2VsZWN0ZWRfaywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQsc2hvd19jbHVzdGVyaW5nPVRydWUpCnVzYWdlX25vcm0sIGdlcF9zY29yZXMsIGdlcF90cG0sIHRvcGdlbmVzID0gY25tZl9vYmoubG9hZF9yZXN1bHRzKEs9c2VsZWN0ZWRfaywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCmBgYAoKYGBge3J9CiMgY2FsY3VsYXRlIHVzYWdlIGJ5IGV4cHJlc3Npb24qZ2VuZXMgY29lZnM6CmdlcF9zY29yZXMgPSBweSRnZXBfc2NvcmVzCmFsbF9tZXRhZ2VuZXM9IHB5JHVzYWdlX25vcm0KYGBgCgojIyBFbnJpY2htZW50IGFuYWx5c2lzIGJ5IHRvcCAyMDAgZ2VuZXMgb2YgZWFjaCBwcm9ncmFtCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTgsIHJlc3VsdHM9J2hpZGUnfQpwbHRfbGlzdCA9IGxpc3QoKQpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMjAwKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIHJlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wLGJhY2tncm91bmQgPSByb3duYW1lcyhnZXBfc2NvcmVzKSxob21lciA9IFQsdGl0bGUgPSAKICAgICAgICAgICAgICAgICAgICBpLHNpbGVudCA9IFQscmV0dXJuX2FsbCA9IFQpCiAgIAogIHBsdF9saXN0W1tpXV0gPSByZXMkcGx0Cn0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBwbHRfbGlzdCkKYGBgCmBgYHtyfQpwbHRfbGlzdCA9IGxpc3QoKQpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMTApICN0YWtlIHRvcCB0b3BfZ2VuZXNfbnVtCiAgbWVzc2FnZShwYXN0ZSgicHJvZ3JhbSAiLGksInRvcCBnZW5lczoiKSkKICBwcmludCh0b3ApCgp9CmBgYAoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQojIE1ha2UgbWV0YWdlbmUgbmFtZXMKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpW2ldID0gIm1ldGFnZW5lLiIgJT4lIHBhc3RlMChpKQp9CgojYWRkIGVhY2ggbWV0YWdlbmUgdG8gbWV0YWRhdGEKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIG1ldGFnZV9tZXRhZGF0YSA9IGFsbF9tZXRhZ2VuZXMgJT4lIHNlbGVjdChpKQogIGFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEpCn0KCkZlYXR1cmVQbG90KG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLGZlYXR1cmVzID0gY29sbmFtZXMoYWxsX21ldGFnZW5lcykpCgpgYGAKIyMgYXNzaWdubWVudCBVTUFQCmBgYHtyfQpsYXJnZXJfYnkgPSAyCm1lc3NhZ2UocGFzdGUoImxhcmdlcl9ieSA9ICIsIGxhcmdlcl9ieSkpCmFjYzFfY2FuY2VyX2NlbGxzID0gcHJvZ3JhbV9hc3NpZ25tZW50KGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscyxsYXJnZXJfYnkgPSBsYXJnZXJfYnkscHJvZ3JhbV9uYW1lcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpKQpzZWxlY3RlZF9rID00CmNvbG9ycyA9ICByYWluYm93KHNlbGVjdGVkX2spCmNvbG9ycyA9IGMoY29sb3JzLCJncmV5IikKRGltUGxvdChhY2MxX2NhbmNlcl9jZWxscyxncm91cC5ieSA9ICJwcm9ncmFtLmFzc2lnbm1lbnQiLHB0LnNpemUgPSAyLGNvbHMgPWNvbG9ycykKYGBgIApzaG93IGNlbGwgY3ljbGUgcHJvZ3JhbToKYGBge3Igd2FybmluZz1GQUxTRX0KaGFsbG1hcmtfbmFtZSA9ICJHT19NSVRPVElDX0NFTExfQ1lDTEUiCmdlbmVzZXRzICA9R1NFQUJhc2U6OmdldEdtdCgiLi9EYXRhL2guYWxsLnY3LjAuc3ltYm9scy5wbHVzY2MuZ210IikKdmFyX2ZlYXR1cmVzPWFjYzFfY2FuY2VyX2NlbGxzQGFzc2F5cyRSTkFAdmFyLmZlYXR1cmVzCmdlbmVJZHM9IGdlbmVzZXRzW1toYWxsbWFya19uYW1lXV1AZ2VuZUlkcwpzY29yZSA8LSBhcHBseShhY2MxX2NhbmNlcl9jZWxsc0Bhc3NheXMkUk5BQGRhdGFbaW50ZXJzZWN0KGdlbmVJZHMsdmFyX2ZlYXR1cmVzKSxdLDIsbWVhbikKYWNjMV9jYW5jZXJfY2VsbHM9QWRkTWV0YURhdGEoYWNjMV9jYW5jZXJfY2VsbHMsc2NvcmUsaGFsbG1hcmtfbmFtZSkKYGBgCmBgYHtyfQpGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGhhbGxtYXJrX25hbWUpCgpgYGAKYGBge3J9CmNjX3ZzX3Byb2dyYW0yID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJtZXRhZ2VuZS4yIixoYWxsbWFya19uYW1lKSkKY29yKGNjX3ZzX3Byb2dyYW0yWzFdLGNjX3ZzX3Byb2dyYW0yWzJdKQpgYGAKCiMjIENvbXBhcmlzaW9ucwoKCmBgYHtyfQpjbnZfdnNfaHB2ID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJjbnYuY2x1c3RlciIsImhwdjMzX3Bvc2l0aXZlIikpCnRlc3QgPC0gZmlzaGVyLnRlc3QodGFibGUoY252X3ZzX2hwdikpCmdnYmFyc3RhdHMoCiAgY252X3ZzX2hwdiwgY252LmNsdXN0ZXIsIGhwdjMzX3Bvc2l0aXZlLAogIHJlc3VsdHMuc3VidGl0bGUgPSBGQUxTRSwKICBzdWJ0aXRsZSA9IHBhc3RlMCgKICAgICJGaXNoZXIncyBleGFjdCB0ZXN0IiwgIiwgcC12YWx1ZSA9ICIsCiAgICBpZmVsc2UodGVzdCRwLnZhbHVlIDwgMC4wMDEsICI8IDAuMDAxIiwgcm91bmQodGVzdCRwLnZhbHVlLCAzKSkKICApCikKCmBgYAoKYGBge3J9CmNudl92c19jbm1mID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJwcm9ncmFtLmFzc2lnbm1lbnQiLCJjbnYuY2x1c3RlciIpKQpjbnZfdnNfY25tZiA9IGNudl92c19jbm1mICU+JSBkcGx5cjo6ZmlsdGVyKHByb2dyYW0uYXNzaWdubWVudCA9PSAiMSIgfHByb2dyYW0uYXNzaWdubWVudCA9PSAiMiIgKQp0ZXN0IDwtIGZpc2hlci50ZXN0KHRhYmxlKGNudl92c19jbm1mKSkKZ2diYXJzdGF0cygKICBjbnZfdnNfY25tZiwgcHJvZ3JhbS5hc3NpZ25tZW50LCBjbnYuY2x1c3RlciwKICByZXN1bHRzLnN1YnRpdGxlID0gRkFMU0UsCiAgc3VidGl0bGUgPSBwYXN0ZTAoCiAgICAiRmlzaGVyJ3MgZXhhY3QgdGVzdCIsICIsIHAtdmFsdWUgPSAiLAogICAgaWZlbHNlKHRlc3QkcC52YWx1ZSA8IDAuMDAxLCAiPCAwLjAwMSIsIHJvdW5kKHRlc3QkcC52YWx1ZSwgMykpCiAgKQopCgpgYGAKCmBgYHtyfQpjbm1mX3ZzX2hwdiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygicHJvZ3JhbS5hc3NpZ25tZW50IiwiaHB2MzNfcG9zaXRpdmUiKSkKY25tZl92c19ocHYgPSBjbm1mX3ZzX2hwdiAlPiUgZHBseXI6OmZpbHRlcihwcm9ncmFtLmFzc2lnbm1lbnQgPT0gIjEiIHxwcm9ncmFtLmFzc2lnbm1lbnQgPT0gIjIiICkKdGVzdCA8LSBmaXNoZXIudGVzdCh0YWJsZShjbm1mX3ZzX2hwdikpCmdnYmFyc3RhdHMoCiAgY25tZl92c19ocHYsIHByb2dyYW0uYXNzaWdubWVudCwgaHB2MzNfcG9zaXRpdmUsCiAgcmVzdWx0cy5zdWJ0aXRsZSA9IEZBTFNFLAogIHN1YnRpdGxlID0gcGFzdGUwKAogICAgIkZpc2hlcidzIGV4YWN0IHRlc3QiLCAiLCBwLXZhbHVlID0gIiwKICAgIGlmZWxzZSh0ZXN0JHAudmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCByb3VuZCh0ZXN0JHAudmFsdWUsIDMpKQogICkKKQoKYGBgCgpgYGB7cn0KbXliX3ZzX2NudiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygiY252LmNsdXN0ZXIiLCJNWUIiKSkKbXliX3ZzX2NudiAkY252LmNsdXN0ZXIgPSBhcy5jaGFyYWN0ZXIobXliX3ZzX2NudiAkY252LmNsdXN0ZXIgKQoKZ2dib3hwbG90KG15Yl92c19jbnYsIHggPSAiY252LmNsdXN0ZXIiLCB5ID0gIk1ZQiIsCiAgICAgICAgICBwYWxldHRlID0gImpjbyIsCiAgICAgICAgICBhZGQgPSAiaml0dGVyIikrIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCIxIiwiMiIpKSkKYGBgCgoKCmBgYHtyfQpteWJfdnNfaHB2ID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJocHYzM19wb3NpdGl2ZSIsIk1ZQiIpKQpteWJfdnNfaHB2ICRocHYzM19wb3NpdGl2ZSA9IGFzLmNoYXJhY3RlcihteWJfdnNfaHB2ICRocHYzM19wb3NpdGl2ZSApCgpnZ2JveHBsb3QobXliX3ZzX2hwdiwgeCA9ICJocHYzM19wb3NpdGl2ZSIsIHkgPSAiTVlCIiwKICAgICAgICAgIHBhbGV0dGUgPSAiamNvIiwKICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSsgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsY29tcGFyaXNvbnMgPSBsaXN0KGMoInBvc2l0aXZlIiwibmVnYXRpdmUiKSxjKCJzdHJvbmcgcG9zaXRpdmUiLCAicG9zaXRpdmUiKSxjKCJzdHJvbmcgcG9zaXRpdmUiLCAibmVnYXRpdmUiKSkpKyBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBmdW5jdGlvbih4KSBkYXRhLmZyYW1lKHk9MTUsIGxhYmVsID0gcGFzdGUoIk1lYW49Iixyb3VuZChtZWFuKHgpLGRpZ2l0cyA9IDIpKSksIGdlb209InRleHQiKSAreWxhYigibG9nMihNWUIpIikKYGBgCgpgYGB7cn0KaHB2UmVhZHNfdnNfbXliID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJIUFYzMy5yZWFkcyIsIk1ZQiIpKQpjb3JyID0gY29yKGhwdlJlYWRzX3ZzX215YiRIUFYzMy5yZWFkcyxocHZSZWFkc192c19teWIkTVlCKQpwcmludCgiY29ycmVsYXRpb24gb2YgTVlCIGFiZCBocHYzM19yZWFkczoiICU+JSBwYXN0ZShjb3JyICU+JSByb3VuZChkaWdpdHMgPSAyKSkgKQpgYGAKCgo=